﻿using System.Net.DnsToolkit.Packet;
using System.Net.DnsToolkit.Serializer.Stream.Extension;
using System.Net.DnsToolkit.Util;
using System.Net.DnsToolkit.Packet.Busy;

namespace System.Net.DnsToolkit.Bastard.PacketGenerator {
    /// <summary>
    /// Creates DNS packet that has domain name compression pointer
    /// that point to inside itself, creating giant matriochkas loop 
    /// 
    /// Some explaination on the bug :
    /// ---------------------------------------------------------
    ///   Normal packet, compression disabled
    /// ---------------------------------------------------------
    /// 
    /// Message {
    ///     Header,
    ///     Questions {
    ///         Question0 {
    ///             Name = "012345678ABCDEF-F-E-D-C-B-A-9-8-7-6-5-4-3-2-1-"
    ///         }
    ///     }
    ///     Answers,
    ///     Authorities,
    ///     Additionals
    /// }
    /// 
    /// 
    /// ---------------------------------------------------------
    ///   Domain pointer matriochkas Loop
    ///   >0 points to 000000000000 offset 0
    ///   >1 points to 000000000000 offset 1 and so on
    ///   x10 means name is size 10
    /// ---------------------------------------------------------
    /// 
    /// Message {
    ///     Header,
    ///     Questions {
    ///         Question0 {
    ///             Name = "012345678ABCDEF>F>E>D>C>B>A>9>8>7>6>5>4>3>2>1"
    ///         }
    ///     }
    ///     Answers,
    ///     Authorities,
    ///     Additionals
    /// }
    /// 
    /// 
    /// 
    /// 
    /// </summary>
    public class DomainPointerMatriochkasLoop : IPacketGenerator {


        private readonly string _name;
        private readonly int _maxloop;
        
        public DomainPointerMatriochkasLoop()
            : this("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 191) {
        }
        
        public DomainPointerMatriochkasLoop(string name, int maxloop) {
            _name = name;
            _maxloop = maxloop;
        }


        public byte[] GetBytes() {

            byte[] bs = GetMessage().GetBytes();

            int beginp = 12;                     // question0.Qname.Name
            int size = bs[beginp];               // save string size
            int endp = (beginp + size);          // compute real end pointer
            
            int loop = 0;
            do {

                bs[beginp] = (byte)--size;             // change name size, -1 to create space for endp
                bs[endp] = CompressionConstant.NameCompression;  // create pointer
                bs[endp + 1] = (byte)++beginp;         // that forward to begining of string +1

                size = (byte)(endp - beginp - 2);      // new size
                endp = (byte)endp - 2;                 // new end pointer
                
                loop++;

            } while (beginp < endp && loop <= _maxloop);

            return bs;
        }

        public IMessage GetMessage() {

            var message = new Message();
            message.Questions.AddRange(
                new Question[] {
                    new Question {
                        QClass = QClassEnum.IN,
                        QType = QTypeEnum.A,
                        QName = new DomainName {
                            Name = _name
                        }
                    }
                }
            );
            message.Header.QuestionCount = (ushort)message.Questions.Count;


            return message;
        }

    }
}
